# Module for (HDLC) - High Level Data Link Control protocol

#########################################################################################
#
# Copyright (C) 2008  
# Authors: Aljosa Osep (aljosa.osep@gmail.com), Jernej Kranjec (jernej.kranjec@gmail.com)
# From Faculty of Electrical Engineering and Computer Science, Maribor, Slovenia
# Done at School of Technology, Seinajoki, Finland
# Under mentor Kimmo Salmenjoki (kimmo.salmenjoki@seamk.fi)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#########################################################################################

# Class for (CHUMP) - Comprehensive Heuristic Unified Messaging Protocol

class CHUMP:
	
	def __init__(self):
		pass
	
	# Function to parse CHUMP results
	def parse(self, inputData):
		# New Dictionary of results
		self.result = {}
		
		# Detecting flag (} - transmitting, { - receiving)
		if inputData[:1] == "}" or inputData[:1] == "{":
			self.result['flag:'] = inputData[:1]
			inputData = inputData[1:]
		
			# Variables used in the process of fishing out parameters and associated data
			self.para = ""	# Parameters go here
			self.data = ""	# Data goes here
			self.pod = 0 	# Parameter Or Data, 0 - saving parameter, 1 - saving data
			
			# Loop trough the rest of result and fish out parameters and associated data
			for i, char in enumerate(inputData):
					
				if char == "," or char == ";" or char == "!":	# "," is a dividing character, ";" and "!" are ending characters
					# First we check for know exceptions in the pattern

					if self.para == ":":			# Result carries the device address
						self.result['addres:'] = self.data
					elif char == ";":				# Result carries checksum
						if self.data == "":	# No need for empty dictionary entries if there is only reply command
							self.result['command:'] = self.para
						else:
							self.result[self.para] = self.data
						self.result['checksum:'] = inputData[i+1:][:2]
						break				# Rest of the string doesn't need to be processed
					elif self.data == "":			# Result carries device command
						self.result['command:'] = self.para
					else:					# Writing standard parameter & data pair to Dictionary
						self.result[self.para] = self.data
					
					# Resetting used variables
					self.para = ""				# Parameters go here
					self.data = ""				# Data goes here
					self.pod = 0 				# Parameter Or Data, 0 - saving parameter, 1 - saving data
				else:
					if self.pod == 0:
						self.para = self.para + char
					else:
						self.data = self.data + char
					if char == ":":
						self.pod = 1
					
		return self.result					# Returning generate Dictionary of parameter & data pairs
	
	
	# Function to generate CHOMP checksum	
	def generateChecksum(self, inputData):
		# New variable to hold the generated checksum in DEC form
		self.FCS = 0
		self.oldFCS = 0
		
		## Detection flag for valid checksum generation
		self.voi = 0						# Valid Or Invalid, Valid - 1. Invalid - 0	

		
		
		# Loop trough and generate checksum
		for char in inputData:
			
			self.FCS = self.FCS + ord(char)	# All the characters are summed together
			
			if char == ";":					# ";" is a valid ending character		
				self.voi = 1				# Change detection flag to valid
				if self.FCS < 0:			# If the generated checksum is negative, the last positive number (even if grater than 128) is used
					self.FCS = self.oldFCS + ord(char)
				break						# Rest of the string doesn't need to be processed
			else:
				if self.FCS > 128:			# Values from -127 to 128 are needed
					self.oldFCS = self.FCS	# Storing last positive number before substracting in case of negative end result
					self.FCS = self.FCS - 256	# Greater values are reduced
			
		if self.voi == 1:
			return hex(self.FCS)[2:].upper()	# Valid flag returns hexadecimal checksum
		else:
			return ""							# Invalid flag returns nothing
	
	
	# Function to verify checksum
	def verifyChecksum(self,inputData):
		# New variable to hold recived checksum in HEX form
		self.FCS = 00
		
		# Loop trough and fish out checksum
		for i, char in enumerate(inputData):
			
			if char == ";":					# ";" is a valid ending character in data that contains a checksum
				self.FCS = inputData[i+1:][:2].upper()
		
		# When recived checksum matches generated, retun walue is 1, when it dosent or is missing the retun value is 0
		if self.FCS == self.generateChecksum(inputData):
			return 1
		else:
			return 0
	
	
	# Function to translate error codes to human redable description		
	def translateError(self,inputData):
		# Python dictionary with Error Code and Description pairs
		self.errCode = {}
		
		# InputingError Codes and Descriptions in to the dictionary
		# Serial Protocol Errors
		self.errCode['0x01'] = "Bad message: point-to-poit only. Problem with message head or tail."
		self.errCode['0x03'] = "Message Synch: point-to-point only. Current message head or \"}\" character interrupted a previous message."
		self.errCode['0x04'] = "Bad CRC: point-to-point only. The last valid message string had a bad CRC."
		self.errCode['0x05'] = "Unprintable bytes rejected."
		self.errCode['0x08'] = "Bad message head."
		self.errCode['0x09'] = "Bad message tail."
		self.errCode['0x0a'] = "Bad or unsupported command."
		self.errCode['0x0b'] = "Bad or unsupported subcommand."
		self.errCode['0x0c'] = "Unsupported command parameter. Command rejected."
		self.errCode['0x0d'] = "Unsupported command parameter. Command proceeding."
		self.errCode['0x0e'] = "High level serial error 1"
		self.errCode['0x0f'] = "High level serial error 2"
		self.errCode['0x10'] = "Command is inhibited."
		# Board Support Errors
		self.errCode['0x21'] = "Temperature sensor error."
		self.errCode['0x22'] = "RAM error."
		self.errCode['0x23'] = "ROM error."
		self.errCode['0x24'] = "Serial number chip error."
		self.errCode['0x25'] = "Antenna missing or bad."
		self.errCode['0x26'] = "Bad antenna mux."
		self.errCode['0x27'] = "RXD reflective power too high. Check antenna or cable connection."
		self.errCode['0x28'] = "RXD reflective power warning level. Occurs at -16 dBm."
		self.errCode['0x29'] = "Listen Before Transmit - all frequencies occupied."
		self.errCode['0x2a'] = "RXD reflective power max fault."
		self.errCode['0x2f'] = "Other self-test error."
		# Radio and Tag Errors
		self.errCode['0x31'] = "Radio controller does not respond or general RF section failure."
		self.errCode['0x33'] = "Antenna failure."
		self.errCode['0x35'] = "No RF field."
		self.errCode['0x36'] = "No tag in field (Rd, Rt, Wa, Wt commands)."
		self.errCode['0x37'] = "Tag data received, but fails integrity checks (Rd, Rt, Wa, Wt commands)."
		self.errCode['0x38'] = "Good tag data available, but specific requested tag not found (Rt, Wt commands)."
		self.errCode['0x39'] = "Attempt to write to a read-only tag or locked block."
		self.errCode['0x3a'] = "Radio lost lock (UHF synthesizer error)."
		# Application Errors
		self.errCode['0x41'] = "Tag data lost (in serial polled or RF continuous mode, incoming new tag data has overwritten old tag data between polls)."
		self.errCode['0x42'] = "Non-volatile memory data element does not exist or not found [when requesting a read of a specific element in Non-volatile memory (NVM)]."
		self.errCode['0x43'] = "NVM is full (when requesting a write to a specific element in NVM)."
		self.errCode['0x44'] = "No NVM present."
		self.errCode['0x45'] = "Invalid configuration variable."
		self.errCode['0x46'] = "Failure of application specific hardware."
		self.errCode['0x48'] = "FPGA version is older than expected."
		self.errCode['0x50'] = "Receiver tuning training failure."
		self.errCode['0x51'] = "Receiver tuning runtime failure."
		self.errCode['0x52'] = "Bad Passcode for Kill Tag function."
		self.errCode['0x53'] = "CHUMP loader doesn't support current Baud rate."
		self.errCode['0x54'] = "CHUMP command failed, reason unknown."
		self.errCode['0x55'] = "Error for trying to overwrite a valid EPC code."
		
		return self.errCode[inputData.lower()]
